package com.google.adwords.samples.disapproved;

import com.google.adwords.samples.disapproved.auth.Login;
import com.google.api.adwords.lib.AdWordsUser;
import com.google.api.adwords.v10.AccountInterface;
import com.google.api.adwords.v10.Ad;
import com.google.api.adwords.v10.AdGroup;
import com.google.api.adwords.v10.AdGroupInterface;
import com.google.api.adwords.v10.AdInterface;
import com.google.api.adwords.v10.ApiError;
import com.google.api.adwords.v10.Campaign;
import com.google.api.adwords.v10.CampaignInterface;
import com.google.api.adwords.v10.GeoTarget;
import com.google.api.adwords.v10.ReportInterface;
import com.google.api.adwords.v10.ReportJob;
import com.google.api.adwords.v10.ReportJobStatus;

import java.util.logging.Logger;

import javax.xml.rpc.ServiceException;

/**
 * The AdWordsInterface handles *all* calls the AdWords API. This is very
 * important because these reports can take several days to run on a very large
 * MCC account. It is common to get intermittent errors with the AdWords API.
 * For example, 100 consecutive calls to the same API method could result in 1
 * error and 99 successes. The AdWordsInterface abstracts these intermittent
 * issues away from the rest of the application.
 * TODO Get rid of recursion in this class.
 * 
 * @author alecmgo@gmail.com (Alec Go)
 * 
 */
public class AdWordsInterface {
  private static Logger logger =
      Logger.getLogger(AdWordsInterface.class.getName());

  // The maximum number of times the AdWords API should be retried
  private final int RETRY_MAX = 5;
  
  //AdWords API interfaces
  private final AccountInterface accountInterface;
  private final ReportInterface reportInterface;
  private CampaignInterface campaignInterface;
  private AdGroupInterface adGroupInterface;
  private AdInterface adInterface;
  
  private final String account;
  private Config config;
  
  //TODO - Convert retryCount to a nonstatic field.
  private static int retryCount = 0;
  
  public AdWordsInterface(String account) throws AppException {
    try {
      this.config = Util.getConfig();
      AdWordsUser user =
          new AdWordsUser(config.getEmail(), config.getPassword(), config
              .getDeveloperToken(), config.getApplicationToken(), config
              .getUserAgent());
      this.accountInterface =
          (AccountInterface) user.getService(AdWordsUser.ACCOUNT_SERVICE);
      this.reportInterface =
          (ReportInterface) user.getService(AdWordsUser.REPORT_SERVICE);
      this.campaignInterface =
          (CampaignInterface) user.getService(AdWordsUser.CAMPAIGN_SERVICE);
      this.adGroupInterface =
          (AdGroupInterface) user.getService(AdWordsUser.AD_GROUP_SERVICE);
      this.adInterface = (AdInterface) user.getService(AdWordsUser.AD_SERVICE);
      
      // If the account is empty or null, take the account that is specified in
      // the properties file
      if ((account != null) && (!account.equals(""))) {
        this.account = account;
      } else {
        this.account = config.getEmail();
      }
    } catch(ServiceException e) {
      throw new AppException(e);
    } 
  }  
  
  /**
   * AccountService - Retrieves the client accounts via the 
   * @return all client accounts
   * @throws AppException
   */
  public String[] getClientAccounts() throws AppException {
    try {
      Login.setHeaders(accountInterface, account, config);
      return accountInterface.getClientAccounts();
    } catch(Exception e) {
      logger.severe(e.toString());
      if(retryCount < RETRY_MAX) {
        retryCount++;
        return getClientAccounts();
      }
      throw new AppException(e.toString());
    }
  }
  
  /**
   * Ad Service - returns all the ads
   * @return all ads
   */
  public Ad[] getAllAds(int[] adGroupIds) throws AppException {
    try {
      Login.setHeaders(adInterface, account, config);
      return adInterface.getAllAds(adGroupIds);
    } catch(Exception e) {
      logger.severe(e.toString());
      
      if(retryCount < RETRY_MAX) {
        retryCount++;
        return getAllAds(adGroupIds);
      }
      throw new AppException(e.toString());
    }
  }
  
  /**
   * Ad Service - checks ads for policy violations
   * @param ads
   * @return the policy error (if any)
   * @throws AppException
   */
  public ApiError[] checkAds(Ad[] ads) throws AppException {
    try {
      Login.setHeaders(adInterface, account, config);
      GeoTarget geoTarget = new GeoTarget();
       geoTarget.setCountryTargets(new String[]{"US"});
      return adInterface.checkAds(ads, new String[]{}, geoTarget);
    } catch(Exception e) {
      logger.severe(e.toString());
      if(retryCount < RETRY_MAX) {
        retryCount++;
        return checkAds(ads);
      }
      throw new AppException(e.toString());
    }
  }
  
  /**
   * AdGroup Service - returns all the adgroups
   * @return all adgroups
   */
  public AdGroup[] getAllAdGroups(int campaignId) throws AppException {
    try {
      Login.setHeaders(adGroupInterface, account, config);
      return adGroupInterface.getAllAdGroups(campaignId);
    } catch(Exception e) {
      logger.severe(e.toString());
      if(retryCount < RETRY_MAX) {
        retryCount++;
        return getAllAdGroups(campaignId);
      }
      throw new AppException(e.toString());
    }
  }
  
  /**
   * Campaign Service - returns all the campaigns
   * @return all campaigns
   */
  public Campaign[] getAllAdWordsCampaigns() throws AppException {
    try {
      Login.setHeaders(campaignInterface, account, config);
      return campaignInterface.getAllAdWordsCampaigns(0);
    } catch(Exception e) {
      logger.severe(e.toString());
      if(retryCount < RETRY_MAX) {
        retryCount++;
        return getAllAdWordsCampaigns();
      }
      throw new AppException(e.toString());
    }
  }
  
  /**
   * ReportService - Schedules a report job
   * @param reportJob
   * @return the jobid of the report
   * @throws AppException
   */
  public long scheduleReportJob(ReportJob reportJob) throws AppException {
    try {
      Login.setHeaders(this.reportInterface, account, config);
      return reportInterface.scheduleReportJob(reportJob);
    } catch(Exception e) {
      logger.severe(e.toString());
      if(retryCount < RETRY_MAX) {
        retryCount++;
        return scheduleReportJob(reportJob);
      }
      throw new AppException(e.toString());
    }
  }

  /**
   * ReportJob - gets the report job status
   * @param jobId
   * @return the job status
   * @throws AppException
   */
  public ReportJobStatus getReportJobStatus(long jobId) throws AppException {
    try {
      Login.setHeaders(this.reportInterface, account, config);
      return reportInterface.getReportJobStatus(jobId);
    } catch(Exception e) {
      logger.severe(e.toString());
      if(retryCount < RETRY_MAX) {
        retryCount++;
        return getReportJobStatus(jobId);
      }
      throw new AppException(e.toString());
    }
  }
  
  /**
   * ReportJob - gets a report download URL
   * @param jobId
   * @return the report download URL
   * @throws AppException
   */
  public String getReportDownloadUrl(long jobId) throws AppException {
    try {
      Login.setHeaders(this.reportInterface, account, config);
      return reportInterface.getReportDownloadUrl(jobId);
    } catch(Exception e) {
      logger.severe(e.toString());
      if(retryCount < RETRY_MAX) {
        retryCount++;
        return getReportDownloadUrl(jobId);
      }
      throw new AppException(e.toString());
    }
  }
}
